Java使用jfreechart生成pdf图表

您所在的位置:网站首页 前端生成pdf包含 echarts Java使用jfreechart生成pdf图表

Java使用jfreechart生成pdf图表

2024-07-03 23:20:43| 来源: 网络整理| 查看: 265

本项目主要技术是jfreechart和itextpdf,纯Java生成pdf图表,我给大家分享一下完成过程中遇到的各种坑.本项目适合于需要生成pdf图表又没有html模板的需求.

我尝试过freemarker,失败在模板制作上.使用word制作模板然后转成html,再使用freemarker根据html模板生成pdf,但word只能制作带表格和文本的模板,折线图这种图表只能作为图片放在模板里,这样就不能插入数据,故而失败.哪位同学知道如何制作图表模板的请不吝赐教.

还尝试过 abel533 / ECharts,我想到每次生成图表的需求可能不一样,所以想到从echarts拿到这些图表直接用,但这个我没能生成,哪位同学想试试的可以试一下

http://hzhcontrols.com/new-566194.html

还尝试过使用js的方式,找前端朋友写一个只有表格和文本的js,测试一下可以导出成pdf,然后将echarts的图表导出为js,嵌入第一个js里,再导出就发现pdf里echarts的位置是一片空白.

给大家提供一个根据html生成pdf的短小精悍的demo:

import java.io.File; import java.io.FileOutputStream; import java.io.OutputStream; import org.xhtmlrenderer.pdf.ITextFontResolver; import org.xhtmlrenderer.pdf.ITextRenderer; import com.lowagie.text.pdf.BaseFont; public class aaa { /** * @param args */ public static void main(String[] args) throws Exception { // Todo Auto-generated method stub String inputFile = "src/main/resources/pdfPage.html"; String url = new File(inputFile).toURI().toURL().toString(); String outputFile = "D://test.pdf"; System.out.println(url); OutputStream os = new FileOutputStream(outputFile); ITextRenderer renderer = new ITextRenderer(); renderer.setDocument(url); // 解决中文支持问题 ITextFontResolver fontResolver = renderer.getFontResolver(); fontResolver.addFont("C:/Windows/Fonts/SimsUN.TTC", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); // 解决图片的相对路径问题 renderer.getSharedContext().setBaseURL("file:/D:/z/temp/"); renderer.layout(); renderer.createPDF(os); os.close(); } }

最后找到了一个用无界浏览器生成的方法,相当于是在java里搭建一个浏览器,然后把页面整个画好,包括图表表格等等,然后把整个页面导出为图片,再转为pdf,整个过程很复杂,感兴趣的同学来找我要吧

正文开始~

开始之前先放一个整体效果

首先是依赖

org.jfree jfreechart 1.5.0 com.itextpdf itext-asian 5.2.0 com.itextpdf font-asian 7.1.13 com.itextpdf itextpdf 5.5.13 com.alibaba fastjson 1.2.83 org.projectlombok lombok commons-io commons-io 2.6 org.apache.commons commons-lang3 org.apache.commons commons-collections4 4.4 org.springframework.boot spring-boot-maven-plugin

然后是测试类

import com.example.util.CreatePdf; import com.example.util.Datas; import com.example.dto.TableValue; import com.itextpdf.text.DocumentException; import org.apache.commons.io.FileUtils; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; @SpringBootTest class Test1ApplicationTests { @Test void GeneratePdf() throws IOException, DocumentException { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); TableValue tableValue = Datas.formData(); // CreateEchartsPdfUtils.createTable(outputStream,tableContentList,tableContentList2,createLinePorts(), tableContentList3,createPiePort(),tableContentList4,createLinePort(),createBarChartsPort(),createBarChartPort(),createStackedBarChartsPort()); CreatePdf.createPdf(outputStream,tableValue); FileUtils.copyInputStreamToFile(new ByteArrayInputStream(outputStream.toByteArray()),new File("D:\\折线图/demo2.pdf")); }

先来个简单表格试试水

1.表格

数据源

import com.example.dto.TableValue; public class Datas { public static TableValue formData() { TableValue tableValue = new TableValue(); tableValue.setIndexValue1("1"); tableValue.setIndexValue2("2"); tableValue.setIndexValue3("3"); tableValue.setIndexValue4("4"); return tableValue; }

主要框架

import com.example.dto.TableValue; import com.example.dto.ThreeLineColor; import com.itextpdf.text.*; import com.itextpdf.text.pdf.PdfWriter; import java.io.ByteArrayOutputStream; import java.io.IOException; public class CreatePdf { public static void createPdf(ByteArrayOutputStream outputStream, TableValue tableValue) throws DocumentException, IOException { // 创建一个文档(默认大小A4,边距36, 36, 36, 36) Document document = new Document(); // 设置文档大小 document.setPageSize(PageSize.A4); // 设置边距,单位都是像素,换算大约1厘米=28.33像素 document.setMargins(50, 50, 50, 50); // 创建writer,通过writer将文档写入磁盘 PdfWriter writer = PdfWriter.getInstance(document, outputStream); // 打开文档,只有打开后才能往里面加东西 document.open(); // 设置作者 document.addAuthor("作者"); // 设置创建者 document.addCreator("创建者"); //设置封面图片 Image image = Image.getInstance("src/main/resources/static/萌猫.jpg"); image.setAlignment(Image.ALIGN_LEFT); image.scaleAbsolute(500, 300); document.add(image); //设置标题 document.add(CreateEchartsPdfUtils.createHead2("生成pdf图表demo", Font.NORMAL, Element.ALIGN_CENTER)); document.add(CreateEchartsPdfUtils.createHead3("01 表格", Font.BOLD, Element.ALIGN_LEFT)); //第一个表格 oneForm(document, tableValue); document.close(); writer.close(); } /** * 文件传输量表格 * * @param * @return */ private static void oneForm(Document document, TableValue tableValue) throws DocumentException, IOException { String[] oneForm = {"抖音", "微博", "虎牙", "小红书"}; CreateEchartsPdfUtils.Form(document, tableValue, 4, oneForm, ThreeLineColor.danhui); } }

多行表格的话传入list即可

输出pdf

需要注意的是,表格里的列数假如设置的是4列,数据源如果不足4个的话,整行就会不显示

2.日历图

日历图其实也是表格,只不过把表格的框线全部隐藏了,并添加了不同的背景颜色

/** * 创建表格内容 * * @param table 表格对象 * @param tableContentList 表格内容对象 */ private static void createTableTwoCellCalendar(PdfPTable table, List tableContentList, BaseColor baseColor) throws DocumentException, IOException { if (ObjectUtils.isEmpty(tableContentList)) { return; } int j = 0; for (int i = 0; i < tableContentList.size(); i++) { TableValue TableValue = tableContentList.get(i); String date = TableValue.getIndexValue1(); if (StringUtils.isBlank(date)) { return; } String[] split = date.split("-"); //计算每月1号是星期几.以周四为例,返回四 int weeksOfDate = DateUtils.getWeeksOfDate(Integer.valueOf(split[0]), Integer.valueOf(split[1]), 1); //返回4 前面就空4格 返回0就不空 if (j < weeksOfDate) { table.addCell(createTableContentCalendar("", "")); j++; i--; continue; } //获取设置表格内容的字体与内容 if (i % 2 != 0) { table.addCell(createTableContentCalendar(split[2], TableValue.getIndexValue2(), TableValue.getIndexValue3(), baseColor)); } else { table.addCell(createTableContentCalendar(split[2], TableValue.getIndexValue2(), TableValue.getIndexValue3())); } } //一行内如果数据不足列数就不显示,为了让他显示,特地写了6个空字符串 table.addCell(createTableContentCalendar("", "")); table.addCell(createTableContentCalendar("", "")); table.addCell(createTableContentCalendar("", "")); table.addCell(createTableContentCalendar("", "")); table.addCell(createTableContentCalendar("", "")); table.addCell(createTableContentCalendar("", "")); } 3.折线图 /** * 生成折线图 * * @param datas 数据 * @param xName X轴 * @param yName Y轴 * @return 生成的折线图byte数组 */ public static void createLinePortImage(Document document, Map datas, String xName, String yName) throws DocumentException, IOException { try { //种类数据集 DefaultCategoryDataset dataset = new DefaultCategoryDataset(); //遍历map for (Map.Entry entry : datas.entrySet()) { String key = entry.getKey(); Map value = entry.getValue(); for (Map.Entry decimalEntry : value.entrySet()) { dataset.setValue(decimalEntry.getValue(), key, decimalEntry.getKey()); } } //创建2D折线图,折线图分水平显示和垂直显示两种 //legend:是否显示图例(对于简单的柱状图必须是false) //tooltips:是否生成工具 //urls:是否生成URL链接 JFreeChart chart = ChartFactory.createLineChart("", xName, yName, dataset, PlotOrientation.VERTICAL, true, true, true); //得到绘图区 setLineRender((CategoryPlot) chart.getPlot(), true, true); ByteArrayOutputStream bas = new ByteArrayOutputStream(); ChartUtils.writeChartAsJPEG(bas, 1.0f, chart, 800, 500, null); com.itextpdf.text.Image image = com.itextpdf.text.Image.getInstance(bas.toByteArray()); image.setAlignment(com.itextpdf.text.Image.ALIGN_CENTER); image.scaleAbsolute(500, 300); document.add(image); } catch (Exception e) { e.printStackTrace(); } } /** * 设置折线图样式 * * @param plot 折线图对象 * @param isShowDataLabels 是否显示数据标题 * @param isShapesVisible 是否显示数据点 */ public static void setLineRender(CategoryPlot plot, boolean isShowDataLabels, boolean isShapesVisible) { plot.setNoDataMessage(NO_DATA_MSG); plot.setInsets(new RectangleInsets(10, 10, 0, 10), false); LineAndShapeRenderer renderer = (LineAndShapeRenderer) plot.getRenderer(); renderer.setDefaultStroke(new BasicStroke(1.5F)); if (isShowDataLabels) { renderer.setDefaultItemLabelsVisible(true); renderer.setDefaultItemLabelGenerator(new StandardCategoryItemLabelGenerator(StandardCategoryItemLabelGenerator.DEFAULT_LABEL_FORMAT_STRING, NumberFormat.getInstance())); renderer.setDefaultPositiveItemLabelPosition(new ItemLabelPosition(ItemLabelAnchor.OUTSIDE1, TextAnchor.BOTTOM_CENTER)); } // 数据点绘制形状 //显示数据点 renderer.setDefaultShapesVisible(isShapesVisible); // renderer.setSeriesShapesVisible(0,isShapesVisible); //开启填充色 renderer.setUseFillPaint(isShapesVisible); //数据点填充的颜色 renderer.setSeriesFillPaint(0, Color.white); //开启外廓线(填充色开启后有效) renderer.setDrawOutlines(isShapesVisible); //使用外廓线颜色 renderer.setUseOutlinePaint(isShapesVisible); //设置外框线颜色 // renderer.setSeriesOutlinePaint(0,Color.yellow); //设置折线的颜色 renderer.setSeriesPaint(0, ThreeLineColor.sGreen); renderer.setSeriesPaint(1, ThreeLineColor.zhaxianlan); renderer.setSeriesPaint(2, ThreeLineColor.zhexianlan); //设置外廓线大小 renderer.setSeriesOutlineStroke(0, new BasicStroke(2.0F)); //设置线条粗细(setDefaultStroke和这个方法冲突) renderer.setSeriesStroke(0, new BasicStroke(1.0F)); //圆形数据点 // renderer.setSeriesShape(0, new Ellipse2D.Double(-2.0D,-2.0D,5.0D,5.0D) ); plot.setRenderer(renderer); setXAixs(plot, CategoryLabelPositions.STANDARD); setYAixs(plot); }

x轴和y轴的设置

/** * 设置类别图表(CategoryPlot) X坐标轴线条颜色和样式 * * @param plot 类别图表对象 categoryLabelPositions x轴文字倾斜度 */ public static void setXAixs(CategoryPlot plot, CategoryLabelPositions categoryLabelPositions) { Color lineColor = new Color(51, 51, 51); //STANDARD:x轴的字体倾斜度 CategoryAxis axis = plot.getDomainAxis(); axis.setCategoryLabelPositions(categoryLabelPositions); // X坐标轴颜色 axis.setAxisLinePaint(lineColor); // X坐标轴标记|竖线颜色 axis.setTickMarkPaint(lineColor); axis.setLabelFont(FONT); // font = Font.createFont(Font.TRUETYPE_FONT,new FileInputStream("/usr/share/fonts/cjkuni-uming/uming.ttc")); axis.setTickLabelFont(FONT); } /** * 设置类别图表(CategoryPlot) Y坐标轴线条颜色和样式 同时防止数据无法显示 * * @param plot 类别图表对象 */ public static void setYAixs(CategoryPlot plot) { Color axisColor = new Color(51, 51, 51); Color tickColor = new Color(219, 218, 218); ValueAxis axis = plot.getRangeAxis(); // Y坐标轴颜色 axis.setAxisLinePaint(axisColor); // Y坐标轴标记|竖线颜色 axis.setTickMarkPaint(tickColor); // false隐藏Y刻度 axis.setAxisLineVisible(true); axis.setTickMarksVisible(true); // Y轴网格线条 plot.setRangeGridlinePaint(new Color(229, 229, 229)); plot.setRangeGridlineStroke(new BasicStroke(1)); // 设置顶部Y坐标轴间距,防止数据无法显示 axis.setUpperMargin(0.1); // 设置底部Y坐标轴间距 axis.setLowerMargin(0.1); axis.setLabelFont(FONT); axis.setTickLabelFont(FONT); //设置y最小取值范围.如果数据全是0,那么0这条折线将显示在y轴中间,y轴0刻度以下将用负数表示, // 但是是显示的是 0E0,-1E-9,-2E-9,-3E-9,-4E-9,这样用科学计数法表示的数,加上这个可 // 以让y轴在数据为0的时候折线在0的水平位置,接近于x轴.但是y轴刻度仍任是科学计数法.不介意的话可以用 //还可以设置最大值,这样的话y轴刻度就不会自动生成了,就是0-1000 /* axis.setLowerBound(0); axis.setUpperBound(1000);*/ }

效果是这样子

大家有没有发现,x轴的字怎么模糊不清呢?是不是字体太小了呢?

放大也是这样,这事因为x轴的内容是 2023-01-01这样的日期,太长了.

将 setLineRender方法末尾的setXAixs(plot, CategoryLabelPositions.STANDARD)修改为CategoryLabelPositions.UP_45,将字体倾斜45度,效果如下

折线和数据点的颜色,数据点的形状等等都是可以自由调节的.

4.饼状图 /** * 生成饼图 * * @param datas 数据 * @return 返回生成的byte数组 */ public static void createPiePortImage(Document document, Map datas) throws DocumentException, IOException { try { // 如果不使用Font,中文将显示不出来 DefaultPieDataset pds = new DefaultPieDataset(); //遍历map for (Map.Entry entry : datas.entrySet()) { pds.setValue(entry.getKey(), entry.getValue()); } /** * 生成一个饼图的图表 * 分别是:显示图表的标题、需要提供对应图表的DateSet对象、是否显示图例、是否生成贴士以及是否生成URL链接 */ JFreeChart chart = ChartFactory.createPieChart("", pds, true, true, true); setPieRender(chart.getPlot()); ByteArrayOutputStream bas = new ByteArrayOutputStream(); ChartUtils.writeChartAsJPEG(bas, 1.0f, chart, 800, 500, null); com.itextpdf.text.Image image = com.itextpdf.text.Image.getInstance(bas.toByteArray()); image.setAlignment(com.itextpdf.text.Image.ALIGN_CENTER); image.scaleAbsolute(500, 300); document.add(image); } catch (Exception e) { e.printStackTrace(); } } /** * 设置饼状图渲染 * * @param plot 饼图对象 */ public static void setPieRender(Plot plot) { plot.setNoDataMessage(NO_DATA_MSG); plot.setInsets(new RectangleInsets(10, 10, 5, 10)); PiePlot piePlot = (PiePlot) plot; piePlot.setInsets(new RectangleInsets(0, 0, 0, 0)); // 圆形 piePlot.setCircular(true); piePlot.setLabelGap(0.01); piePlot.setInteriorGap(0.05D); // 图例形状 piePlot.setLegendItemShape(new Rectangle(10, 10)); piePlot.setIgnoreNullValues(true); // 去掉背景色 piePlot.setLabelBackgroundPaint(null); // 去掉阴影 piePlot.setLabelShadowPaint(null); // 去掉边框 piePlot.setLabelOutlinePaint(null); piePlot.setShadowPaint(null); // 0:category 1:value:2 :percentage // 显示标签数据 {0} 代表 饼状图分类的名字,{1} 显示具体数值,{2} 显示百分比 //{0}:{1} 英雄联盟:20 {0}:{2} 英雄联盟 20% piePlot.setLabelGenerator(new StandardPieSectionLabelGenerator("{0}:{1}")); }

饼状图挺简单的,需要注意的就是值的显示 {0}:{1}显示的就是

{0}:{2}显示的就是百分比

柱状图 /** * 生成柱状图 * * @param datas 数据 * @return */ public static void createBarChartImage(Document document, Map datas) throws DocumentException, IOException { try { //种类数据集 DefaultCategoryDataset dataset = new DefaultCategoryDataset(); //遍历map for (Map.Entry entry : datas.entrySet()) { String key = entry.getKey(); Map value = entry.getValue(); for (Map.Entry decimalEntry : value.entrySet()) { dataset.setValue(decimalEntry.getValue(), key, decimalEntry.getKey()); } } //legend:是否显示图例(对于简单的柱状图必须是false) //tooltips:是否生成工具 //urls:是否生成URL链接 JFreeChart chart = ChartFactory.createBarChart("", "", "", dataset, PlotOrientation.VERTICAL, true, true, false); //得到绘图区 setBarChartRender((CategoryPlot) chart.getPlot(), true, true); ByteArrayOutputStream bas = new ByteArrayOutputStream(); ChartUtils.writeChartAsJPEG(bas, 1.0f, chart, 800, 500, null); com.itextpdf.text.Image image = com.itextpdf.text.Image.getInstance(bas.toByteArray()); image.setAlignment(com.itextpdf.text.Image.ALIGN_CENTER); image.scaleAbsolute(500, 300); document.add(image); } catch (Exception e) { e.printStackTrace(); } } /** * 设置柱状图样式 * * @param plot 折线图对象 * @param isShowDataLabels 是否显示数据标题 * @param isShapesVisible 是否显示数据点 */ public static void setBarChartRender(CategoryPlot plot, boolean isShowDataLabels, boolean isShapesVisible) { plot.setNoDataMessage(NO_DATA_MSG); plot.setInsets(new RectangleInsets(10, 10, 0, 10), false); BarRenderer renderer = (BarRenderer) plot.getRenderer(); renderer.setDefaultStroke(new BasicStroke(0F)); if (isShowDataLabels) { renderer.setDefaultItemLabelsVisible(true); renderer.setDefaultItemLabelGenerator(new StandardCategoryItemLabelGenerator(StandardCategoryItemLabelGenerator.DEFAULT_LABEL_FORMAT_STRING, NumberFormat.getInstance())); renderer.setDefaultPositiveItemLabelPosition(new ItemLabelPosition(ItemLabelAnchor.OUTSIDE12, TextAnchor.BASELINE_CENTER)); } renderer.setDefaultSeriesVisible(true); //设置柱子的宽度 renderer.setMaximumBarWidth(0.05); //设置柱子的高度 // renderer.setMinimumBarLength(0.2); //设置柱子边框线可见 // renderer.setDrawBarOutline(isShapesVisible); //设置柱子边框线的颜色 // renderer.setSeriesOutlinePaint(0, Color.green); //设置每个地区所包含的平行柱的距离 // renderer.setItemMargin(0.5); //显示每个柱的数值,并修改该数值的字体属性 renderer.setIncludeBaseInRange(true); //柱子的透明度 plot.setForegroundAlpha(1.0f); //柱子的文字偏离值 // renderer.setItemLabelAnchorOffset(10D); //设置柱子的颜色 renderer.setSeriesPaint(0, Color.yellow); //设置外廓线大小 /* renderer.setSeriesOutlineStroke(0,new BasicStroke(2.0F)); //设置线条粗细 renderer.setSeriesStroke(0,new BasicStroke(2.0F));*/ setXAixs(plot, CategoryLabelPositions.STANDARD); setYAixs(plot); }

柱子的颜色,透明度,都是可以设置的,字体也可以设置为柱体内,柱体上,柱体旁

6.堆叠柱状图 JFreeChart chart = ChartFactory.createStackedBarChart("", "", "", dataset, PlotOrientation.VERTICAL, true, true, false); //得到绘图区

只需将ChartFactory的createBarChart改为createStackedBarChart就可以

7.柱状图和折线图的组合图

第一种是y轴分离,相当于两个图合在一起,共享x轴

/** * 生成柱状图混合折线图 * 柱状图和折线图共享x轴,y轴是分隔开的,相当于是柱状图上面有个折线图. * @param datas 数据 * @return */ public static void createBarChartAndLineImage(Document document, Map datas) throws DocumentException, IOException { try { //种类数据集 DefaultCategoryDataset dataset = new DefaultCategoryDataset(); //遍历map for (Map.Entry entry : datas.entrySet()) { String key = entry.getKey(); Map value = entry.getValue(); for (Map.Entry decimalEntry : value.entrySet()) { dataset.setValue(decimalEntry.getValue(), key, decimalEntry.getKey()); } } //创建数据源一的纵坐标对象。 NumberAxis rangeAxis1 = new NumberAxis("Value"); LineAndShapeRenderer renderer1 = new LineAndShapeRenderer(); //我们在这里将横坐标设置为NULL,表示不使用自己的横坐标对象,只使用自己的纵坐标和图表渲染对象(折线图) CategoryPlot subplot1 = new CategoryPlot(dataset, null, rangeAxis1, renderer1); //纵坐标对象。 NumberAxis rangeAxis2 = new NumberAxis("Value"); //柱状图的图表渲染对象。 BarRenderer renderer2 = new BarRenderer(); //同样的不是用横坐标对象。 CategoryPlot subplot2 = new CategoryPlot(dataset, null, rangeAxis2, renderer2); CategoryAxis domainAxis = new CategoryAxis("Category"); //先合并横坐标。 CombinedDomainCategoryPlot plot = new CombinedDomainCategoryPlot( domainAxis); //添加纵坐标。 plot.add(subplot1, 2); //添加纵坐标。 plot.add(subplot2, 1); JFreeChart result = new JFreeChart(plot); ByteArrayOutputStream bas = new ByteArrayOutputStream(); ChartUtils.writeChartAsJPEG(bas, 1.0f, result, 800, 500, null); // ChartUtils.writeChartAsJPEG(bas, 1.0f, line, 800, 500, null); com.itextpdf.text.Image image = com.itextpdf.text.Image.getInstance(bas.toByteArray()); image.setAlignment(com.itextpdf.text.Image.ALIGN_CENTER); image.scaleAbsolute(500, 300); document.add(image); } catch (Exception e) { e.printStackTrace(); } }

第二种是双y轴,折线图和柱状图有交互的组合图

/** * 生成柱状图混合折线图 *双y轴,柱状图和折线图在一张图里 * @param datas 数据 * @return */ public static void createChart(Document document, Map datas) throws DocumentException, IOException { try { //种类数据集 DefaultCategoryDataset dataset = new DefaultCategoryDataset(); //遍历map for (Map.Entry entry : datas.entrySet()) { String key = entry.getKey(); Map value = entry.getValue(); for (Map.Entry decimalEntry : value.entrySet()) { dataset.setValue(decimalEntry.getValue(), key, decimalEntry.getKey()); } } // 初始化一个基础渲染规则为3D模式的柱状统计图效果的Chart图表。 JFreeChart chart = ChartFactory.createBarChart("DoubleBarChart", "Category", "Value", dataset, PlotOrientation.VERTICAL, true, true, false); // 获取绘图区对象 CategoryPlot plot = (CategoryPlot) chart.getPlot(); // 设置轴1--数据匹配 NumberAxis axis0 = new NumberAxis("第一条轴线"); plot.setRangeAxis(0, axis0); plot.setDataset(0, dataset); plot.mapDatasetToRangeAxis(0, 0); // 重新生成一个图表渲染的对象(折线图渲染对象)。 LineAndShapeRenderer lineandshaperenderer = new LineAndShapeRenderer(); // 显示折点数据。 lineandshaperenderer.setDefaultItemLabelsVisible(true); lineandshaperenderer.setDefaultItemLabelGenerator(new StandardCategoryItemLabelGenerator()); // 设置拐点是否可见/是否显示拐点 lineandshaperenderer.setDefaultShapesVisible(true); // 设置线条是否被显示填充颜色 lineandshaperenderer.setUseFillPaint(true); // 设置第一条折线的拐点颜色 lineandshaperenderer.setSeriesFillPaint(0, Color.BLUE); // 设置第二条折线的拐点颜色 lineandshaperenderer.setSeriesFillPaint(1, Color.RED); //设置折线颜色(第一条折线数据线) lineandshaperenderer.setSeriesPaint(0, new Color(91, 155, 213)); //设置折线颜色(第二条折折线据线) lineandshaperenderer.setSeriesPaint(1, Color.RED); // 设置第一条折线的广度(粗细度) lineandshaperenderer.setSeriesStroke(0, new BasicStroke(1.8F)); // 设置第二条折线的广度(粗细度) lineandshaperenderer.setSeriesStroke(1, new BasicStroke(1.8F)); //设置拐点数值颜色,默认黑色 lineandshaperenderer.setDefaultItemLabelsVisible(true); // 默认就是true,这里可以不用刻意声明。 lineandshaperenderer.setDefaultItemLabelPaint(Color.BLUE); // 解决最高柱体或折点提示内容被遮盖的问题。 lineandshaperenderer.setDefaultPositiveItemLabelPosition(new ItemLabelPosition(ItemLabelAnchor.OUTSIDE12, TextAnchor.BASELINE_CENTER)); lineandshaperenderer.setItemLabelAnchorOffset(2); // 设置柱形图上的文字偏离值 // 重构第二个数据对象的渲染方式,由现在默认的Bar(柱状统计图)重构为刚刚初始化的Line(折线统计图)的渲染模式 plot.setRenderer(1, lineandshaperenderer); // 设置轴2--数据匹配 NumberAxis axis1 = new NumberAxis("第二条轴线"); plot.setRangeAxis(1, axis1); plot.setDataset(1, dataset); plot.mapDatasetToRangeAxis(1, 1); plot.setDatasetRenderingOrder(DatasetRenderingOrder.FORWARD); /** ---------------------- 中文乱码问题处理 Start ------------------------------- */ CategoryAxis domainAxis = plot.getDomainAxis(); //水平底部列表 domainAxis.setLabelFont(new Font("黑体", Font.BOLD, 14)); //水平底部标题 domainAxis.setTickLabelFont(new Font("宋体", Font.BOLD, 12)); //垂直标题 ValueAxis rangeAxis = plot.getRangeAxis();//获取柱状 rangeAxis.setLabelFont(new Font("黑体", Font.BOLD, 15)); chart.getLegend().setItemFont(new Font("黑体", Font.BOLD, 15)); chart.getTitle().setFont(new Font("宋体", Font.BOLD, 20));//设置标题字体 /** ---------------------- 中文乱码问题处理 End ------------------------------- */ rangeAxis.setAutoRange(true); // 设置图表控件的背景颜色。 chart.setBackgroundPaint(Color.WHITE); ByteArrayOutputStream bas = new ByteArrayOutputStream(); ChartUtils.writeChartAsJPEG(bas, 1.0f, chart, 800, 500, null); // ChartUtils.writeChartAsJPEG(bas, 1.0f, line, 800, 500, null); com.itextpdf.text.Image image = com.itextpdf.text.Image.getInstance(bas.toByteArray()); image.setAlignment(com.itextpdf.text.Image.ALIGN_CENTER); image.scaleAbsolute(500, 300); document.add(image); } catch (Exception e) { e.printStackTrace(); } }

更多组合形式请前往https://yveshe.blog.csdn.net/article/details/81560666?spm=1001.2101.3001.6650.4&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-4-81560666-blog-91975252.pc_relevant_recovery_v2&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-4-81560666-blog-91975252.pc_relevant_recovery_v2&utm_relevant_index=4

有这种的

tips1

每一个图表,我都是先写标题,再画图表

document.add(CreateEchartsPdfUtils.createHead3("01 表格", Font.BOLD, Element.ALIGN_LEFT)); //第一个表格 oneForm(document, tableValue); //日历图 document.add(CreateEchartsPdfUtils.createHead3("02 日历图", Font.BOLD, Element.ALIGN_LEFT)); CreateEchartsPdfUtils.calendarTwoForm(document, calendar); //折线图 document.add(CreateEchartsPdfUtils.createHead3("03 折线图", Font.BOLD, Element.ALIGN_LEFT)); JFreeChartUtils.createLinePortImage(document, linePorts, "日期", "数量"); //饼状图 document.add(CreateEchartsPdfUtils.createHead3("04 饼状图", Font.BOLD, Element.ALIGN_LEFT)); JFreeChartUtils.createPiePortImage(document, piePort); //柱状图 document.add(CreateEchartsPdfUtils.createHead3("05 柱状图", Font.BOLD, Element.ALIGN_LEFT)); JFreeChartUtils.createBarChartImage(document, barChartPort); //堆叠柱状图 document.add(CreateEchartsPdfUtils.createHead3("06 堆叠柱状图", Font.BOLD, Element.ALIGN_LEFT)); JFreeChartUtils.createStackedBarChart(document, stackedBarChartsPort); //柱状图和折线图组合图1 document.add(CreateEchartsPdfUtils.createHead3("07 柱状图和折线图组合图1", Font.BOLD, Element.ALIGN_LEFT)); JFreeChartUtils.createBarChartAndLineImage(document, linePorts); //柱状图和折线图组合图2 双y轴 document.add(CreateEchartsPdfUtils.createHead3("08 柱状图和折线图组合图2", Font.BOLD, Element.ALIGN_LEFT)); JFreeChartUtils.createChart(document, linePorts);

但是实际结果可能是这样的

这可能是jfreechart为了节省空间自动做的排版,遇到这样的情况大家可以先开发,开发完成后统一处理,可以在柱状图代码后加上document.newPage();

tips2

上linux测试环境进行测试后,遇到一个关于字体的问题

private static Font createFont(int fontSize, int fontMode, BaseColor fontColor) throws DocumentException, IOException { BaseFont bfChinese = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED); return new Font(bfChinese, fontSize, fontMode, fontColor); }

这是创建标题时createHead3 所用的,Font是itext下的包,测试环境可以正常创建标题,正常显示

而折线图的x轴所用的字体是awt的字体

private static final Font FONT = new Font("STSongStd-Light", Font.BOLD, 12);

awt的字体在测试环境找不到,所以自动找了个默认字体,贼丑,而且不清晰.

和itext的font相比,awt的font没有编码方式

只找到了itext的字体转awt字体的方法,没有找到awt转itext的方法

试过直接写字体文件名,写字体绝对路径,将字体转成流,都不行.最后将时间由2023-01-02格式改为日期 02,CategoryLabelPositions.UP_45修改为STANDARD,勉强再用.如果有懂的大佬指导下我应该怎么解决,如果你也遇到这个问题不知道怎么解决的话可以用我这个方法

tips3

大家生成pdf肯定都是调用好几个接口,等候接口返回数据再调下一个接口,整体响应时间太长,可以用CompletableFuture进行异步调用

原来取数据:

@Test void GeneratePdf() throws IOException, DocumentException { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); TableValue tableValue = Datas.formData(); List calendar = Datas.calendar(); Map linePorts = Datas.createLinePorts(); Map piePort = Datas.createPiePort(); Map barChartPort = Datas.createBarChartPort(); Map stackedBarChartsPort = Datas.createStackedBarChartsPort(); // CreateEchartsPdfUtils.createTable(outputStream,tableContentList,tableContentList2,createLinePorts(), tableContentList3,createPiePort(),tableContentList4,createLinePort(),createBarChartsPort(),createBarChartPort(),createStackedBarChartsPort()); CreatePdf.createPdf(outputStream,tableValue,calendar,linePorts,piePort,barChartPort,stackedBarChartsPort); FileUtils.copyInputStreamToFile(new ByteArrayInputStream(outputStream.toByteArray()),new File("D:\\折线图/demo2.pdf")); }

使用CompletableFuture以后

@Test void GeneratePdf() throws IOException, DocumentException { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); // TableValue tableValue = Datas.formData(); CompletableFuture tableValue = CompletableFuture.supplyAsync(() -> Datas.formData()); // List calendar = Datas.calendar(); CompletableFuture calendar = CompletableFuture.supplyAsync(() -> Datas.calendar()); // Map linePorts = Datas.createLinePorts(); CompletableFuture linePorts = CompletableFuture.supplyAsync(() -> Datas.createLinePorts()); // Map piePort = Datas.createPiePort(); CompletableFuture piePort = CompletableFuture.supplyAsync(() -> Datas.createPiePort()); // Map barChartPort = Datas.createBarChartPort(); CompletableFuture barChartPort = CompletableFuture.supplyAsync(() -> Datas.createBarChartPort()); // Map stackedBarChartsPort = Datas.createStackedBarChartsPort(); CompletableFuture stackedBarChartsPort = CompletableFuture.supplyAsync(() -> Datas.createStackedBarChartsPort()); CompletableFuture.allOf(tableValue,calendar,linePorts,piePort,barChartPort,stackedBarChartsPort); // CreateEchartsPdfUtils.createTable(outputStream,tableContentList,tableContentList2,createLinePorts(), tableContentList3,createPiePort(),tableContentList4,createLinePort(),createBarChartsPort(),createBarChartPort(),createStackedBarChartsPort()); CreatePdf.createPdf(outputStream,tableValue.join(),calendar.join(),linePorts.join(),piePort.join(),barChartPort.join(),stackedBarChartsPort.join()); FileUtils.copyInputStreamToFile(new ByteArrayInputStream(outputStream.toByteArray()),new File("D:\\折线图/demo2.pdf")); }

tips4

以上都是直接将文件在本地生成,实际使用中需要前端发送请求,我们将文件流给前端返回,该怎么做呢

public void generateEDBPdf(HttpServletResponse response) throws IOException { String name = "要定义的文件名称"; response.setHeader("Content-Disposition", "attachment;filename=" + name + ".pdf"); response.setHeader("Access-Control-Expose-Headers", "Content-Disposition"); response.setContentType("application/pdf;charset=UTF-8"); ServletOutputStream output = response.getOutputStream(); BufferedOutputStream outputStream = new BufferedOutputStream(output); // CreatePdf.createPdf(outputStream,tableValue.join(),calendar.join(),linePorts.join(),piePort.join(),barChartPort.join(),stackedBarChartsPort.join()); }

将CreatePdf.createPdf这个方法里的outputStream改为这个controller对应的Stream就好了

项目git地址 :https://gitee.com/mhjo/minghe/tree/jfree/

大家想推代码请新建分支~

原文链接:https://blog.csdn.net/lol19950605/article/details/128929870



【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭